#ifndef __M_CONNECTION_H__
#define __M_CONNECTION_H__

#include "muduo/proto/dispatcher.h"
#include "muduo/proto/codec.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpClient.h"
#include "muduo/net/EventLoopThread.h"
#include "muduo/base/CountDownLatch.h"

#include "mq_channel.hpp"
#include "mq_worker.hpp"

namespace castlemq
{
    // muduo::CountDownLatch _latch;//实现同步
    // muduo::net::TcpClient _client;//客户端
    // muduo::net::TcpConnectionPtr _conn;//客户端对应的连接
    // ProtobufDispatcher _dispatcher;//请求分发器
    // ProtobufCodecPtr _codec;//协议处理器

    // ChannelManager::ptr _channel_manager;
    // AsyncWorker::ptr _worker;
    class Connection
    {
    public:
        using ptr = std::shared_ptr<Connection>;
        Connection(const std::string sip,int sport,const AsyncWorker::ptr& worker)
        :_latch(1),
        _dispatcher(std::bind(&Connection::onUnknownMessage, this, 
        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),
        _codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher,
        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))),
        _client(worker->loopthread.startLoop(),muduo::net::InetAddress(sip,sport),"Client"),
        _worker(worker),
        _channel_manager(std::make_shared<ChannelManager>())
        {
            //注册业务处理函数
            _dispatcher.registerMessageCallback<castlemq::basicCommonResponse>(std::bind(&Connection::basicResponse,this,
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<castlemq::basicConsumeResponse>(std::bind(&Connection::consumeResponse,this,
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _client.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(), 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            
            _client.setConnectionCallback(std::bind(&Connection::onConnection, this, std::placeholders::_1));

            //连接服务器，需要阻塞等待连接建立之后再返回
            _client.connect();
            _latch.wait();
        }

        Channel::ptr openChannel()
        {
            Channel::ptr channel = _channel_manager->create(_conn,_codec);
            bool ret=channel->openChannel();
            if(!ret)
            {
                DLOG("打开信道失败!");
                return Channel::ptr();
            }
            return channel;
        }
        void closeChannel(const Channel::ptr& channel)
        {
            channel->closeChannel();
            _channel_manager->remove(channel->cid());
            return;
        }
    private:
        void basicResponse(const muduo::net::TcpConnectionPtr& conn,
                            const basicCommonResponsePtr& message,
                            muduo::Timestamp)
        {
            //1.找到信道
            Channel::ptr channel=_channel_manager->get(message->cid());
            if(!channel.get())
            {
                DLOG("没有信道信息！");
                return;
            }
            //2.将得到的响应对象，添加到信道的基础响应hash_map中
            channel->putBasicResponse(message);
        }

        void consumeResponse(const muduo::net::TcpConnectionPtr& conn,
                            const basicConsumeResponsePtr& message,
                            muduo::Timestamp)
        {
            //1.先找到信道
            Channel::ptr channel=_channel_manager->get(message->cid());
            if(!channel.get())
            {
                DLOG("没有信道信息！");
                return;
            }
            //2.封装异步任务（消息处理任务），抛入线程池
            _worker->pool.push([channel,message]()
            {
                channel->consume(message);
            });
        }

        void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn,
                            const MessagePtr& message,
                            muduo::Timestamp)
        {
            LOG_INFO << "onUnknownMessage: " << message->GetTypeName();
            conn->shutdown();
        }

        // 新连接建立成功时的回调函数，需要唤醒上边的阻塞
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            std::cout << "onConnection ...." << std::endl;
            // 新连接建立成功时的回调函数
            if(conn->connected())
            {
                //唤醒主线程的阻塞
                _conn=conn;
                _latch.countDown(); //唤醒主线程
            }
            else
            {
                _conn.reset();  
            }
        }

    private:
        muduo::CountDownLatch _latch;//实现同步
        muduo::net::TcpConnectionPtr _conn;//客户端对应的连接
        muduo::net::TcpClient _client;//客户端
        
        ProtobufDispatcher _dispatcher;//请求分发器
        ProtobufCodecPtr _codec;//协议处理器

        ChannelManager::ptr _channel_manager;
        AsyncWorker::ptr _worker;
    };
}

#endif